grammar graal;

options {output=AST;}

tokens {TERMINAL;NTERMINAL;ELEMENT;ALTERNATIVE;RESERVED;SIGN;SEQUENCE;TERMINALT;NTERMINALT;RESERVEDT;SIGNT;SEQUENCET;OTHER;OTHERT;}

grammar_sq 
  : (rule)+  
  ;

//não-terminal começa por minuscula
//terminal começa com maiuscula

rule
  : i=identifier ':' 
    {int flag = ($i.id.charAt(0) >= 'A' && $i.id.charAt(0) <= 'Z')  ? 1 : 0; }
    alternatives[flag] ';'  -> { flag==1 }? ^( TERMINAL $i alternatives )
				       -> ^(NTERMINAL $i alternatives)	   
;

identifier
  returns[String id]
  : ID { $id = $ID.text; } 
  ;

alternatives[int flag]
  : elements[$flag] ('|' elements[flag] )* -> ^(ALTERNATIVE elements)+
  ;

elements[int flag]
  : element[$flag]+ -> ^(ELEMENT element)+   
  ;

element[int flag]
@init{ int til = 0; } 
  : ('~' { til = 1; } )? s=single (multiplier)?  
				   -> { $s.flag==1 && $s.id.charAt(0) >= 'A' && $s.id.charAt(0) <= 'Z' && til == 0 }? ^( TERMINAL $s multiplier? )
				   -> { $s.flag==1 && $s.id.charAt(0) >= 'A' && $s.id.charAt(0) <= 'Z' && til == 1 }? ^( TERMINALT $s multiplier? )
  				   -> { $s.flag==1 && $s.id.charAt(0) >= 'a' && $s.id.charAt(0) <= 'z' && til == 0 }? ^( NTERMINAL $s multiplier?)
                                   -> { $s.flag==1 && $s.id.charAt(0) >= 'a' && $s.id.charAt(0) <= 'z' && til == 1 }? ^( NTERMINALT $s multiplier?)
  				   -> { $s.flag==0 && flag==0 && $s.id.matches("\\w+") && til == 0 }? ^( RESERVED $s multiplier?)
				   -> { $s.flag==0 && flag==0 && $s.id.matches("\\w+") && til == 1 }? ^( RESERVEDT $s multiplier?)
  				   -> { $s.flag==0 && flag==0 && $s.id.matches("\\W+") && til == 0 }? ^( SIGN $s multiplier?)
 				   -> { $s.flag==0 && flag==0 && $s.id.matches("\\W+") && til == 1 }? ^( SIGNT $s multiplier?)  
  				   -> { til == 0 }? ^( OTHER $s multiplier?)
 				   -> { til == 1 }? ^( OTHERT $s multiplier?)
 				   -> single (multiplier)?    
  | (('~' { til = 1; } )? '('alternatives[$flag] ')' (multiplier)?) 
						      -> { $flag==0 && til == 0}? ^( SEQUENCE alternatives multiplier?)
						      -> { $flag==0 && til == 1}? ^( SEQUENCET alternatives multiplier?)
  						      -> { til == 0 }? ^( OTHER alternatives multiplier?)
 				  		      -> { til == 1 }? ^( OTHERT alternatives multiplier?)
 				  		      -> alternatives multiplier?	 
  ;

multiplier   
  : '*'
  | '+'
  | '?'
  ;

single
  returns[String id,int flag] 
  : s=STRING ('..' STRING)? {$flag=0; $id=$s.text;}
  | i=identifier { $id= $i.id; $flag=1;}              
  ;


ID
  : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
  ;

STRING
    :  '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
	|	'\'' ( ESC_SEQ | ~('\\'|'\'') )* '\''
    ;

fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;

fragment
ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   UNICODE_ESC
    |   OCTAL_ESC
    ;

fragment
OCTAL_ESC
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;

fragment
UNICODE_ESC
    :   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
    ;


WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {$channel=HIDDEN;}
	;
